/**
 * 数字操作工具
 */
// 生成指定范围随机数
export const randomNum = (min: number, max: number): number => {
  return Math.floor(Math.random() * (max - min + 1)) + min;
};

// 数字千分位分隔
export const formatThousands = (n: number | string): string => {
  const num = typeof n === "number" ? n.toString() : n;
  const len = num.length;
  if (len <= 3) return num;

  const remainder = len % 3;
  if (remainder > 0) {
    return (
      num.slice(0, remainder) +
        "," +
        num.slice(remainder).match(/\d{3}/g)?.join(",") || num
    );
  } else {
    return num.match(/\d{3}/g)?.join(",") || num;
  }
};

/**
 * 数组操作工具
 */
// 数组乱序
export const arrScrambling = <T = any>(arr: T[]): T[] => {
  const newArr = [...arr]; // 避免修改原数组
  for (let i = 0; i < newArr.length; i++) {
    const randomIndex = Math.round(Math.random() * (newArr.length - 1 - i)) + i;
    [newArr[i], newArr[randomIndex]] = [newArr[randomIndex], newArr[i]];
  }
  return newArr;
};

// 数组扁平化
export const flatten = <T = any>(arr: any[]): T[] => {
  let result: T[] = [];
  for (let i = 0; i < arr.length; i++) {
    if (Array.isArray(arr[i])) {
      result = result.concat(flatten(arr[i]));
    } else {
      result.push(arr[i] as T);
    }
  }
  return result;
};

// 数组中获取随机元素
export const sample = <T = any>(arr: T[]): T | undefined => {
  if (!Array.isArray(arr) || arr.length === 0) return undefined;
  return arr[Math.floor(Math.random() * arr.length)];
};
/**
 * 字符串操作工具
 */
// 生成随机字符串
export const randomString = (len: number): string => {
  if (len <= 0) return ""; // 处理无效长度
  const chars = "ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz123456789";
  const strLen = chars.length;
  let randomStr = "";
  for (let i = 0; i < len; i++) {
    randomStr += chars.charAt(Math.floor(Math.random() * strLen));
  }
  return randomStr;
};

// 字符串首字母大写
export const firstLetterUpper = (str: string): string => {
  if (typeof str !== "string" || str.length === 0) return str;
  return str.charAt(0).toUpperCase() + str.slice(1);
};

// 手机号中间四位变成*
export const telFormat = (tel: number | string): string => {
  const telStr = String(tel).trim();
  // 仅处理11位手机号
  if (!/^\d{11}$/.test(telStr)) return telStr;
  return telStr.substr(0, 3) + "****" + telStr.substr(7);
};

// 驼峰命名转换成短横线命名
export const getKebabCase = (str: string): string => {
  if (typeof str !== "string") return "";
  return str.replace(/[A-Z]/g, (item) => "-" + item.toLowerCase());
};

// 短横线命名转换成驼峰命名
export const getCamelCase = (str: string): string => {
  if (typeof str !== "string") return "";
  return str.replace(/-([a-z])/g, (_, item) => item.toUpperCase());
};

// 全角转换为半角
export const toCDB = (str: string): string => {
  if (typeof str !== "string") return "";
  return str
    .replace(/[\uff01-\uff5e]/g, (char) => {
      // 全角字符转半角（除空格）
      return String.fromCharCode(char.charCodeAt(0) - 65248);
    })
    .replace(/\u3000/g, " "); // 全角空格转半角
};

// 半角转换为全角
export const toDBC = (str: string): string => {
  if (typeof str !== "string") return "";
  return str
    .replace(/[\x21-\x7e]/g, (char) => {
      // 半角字符转全角（除空格）
      return String.fromCharCode(char.charCodeAt(0) + 65248);
    })
    .replace(/\s/g, "\u3000"); // 半角空格转全角
};

// 去除所有空格
export const trimAll = (str: string): string => {
  return typeof str === "string" ? str.replace(/\s+/g, "") : "";
};

// 去除前后空格
export const trim = (str: string): string => {
  return typeof str === "string" ? str.trim() : "";
};
/**
 * 格式转化工具
 */
// 数字转化为大写金额
export const digitUppercase = (n: number): string => {
  if (isNaN(n)) return "无效数字";
  if (n === 0) return "零元整";
  const isNegative = n < 0;
  const absN = Math.abs(n);
  const fraction = ["角", "分"];
  const digit = ["零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"];
  const unit = [
    ["元", "万", "亿"],
    ["", "拾", "佰", "仟"],
  ];

  let s = "";
  // 处理小数部分
  for (let i = 0; i < fraction.length; i++) {
    s += (
      digit[Math.floor(absN * 10 * Math.pow(10, i)) % 10] + fraction[i]
    ).replace(/零./, "");
  }
  s = s || "整";
  // 处理整数部分
  let integer = Math.floor(absN);
  for (let i = 0; i < unit[0].length && integer > 0; i++) {
    let p = "";
    for (let j = 0; j < unit[1].length && integer > 0; j++) {
      p = digit[integer % 10] + unit[1][j] + p;
      integer = Math.floor(integer / 10);
    }
    s = p.replace(/(零.)*零$/, "").replace(/^$/, "零") + unit[0][i] + s;
  }
  // 处理负数
  const result = s
    .replace(/(零.)*零元/, "元")
    .replace(/(零.)+/g, "零")
    .replace(/^整$/, "零元整");
  return isNegative ? `负${result}` : result;
};

// 数字转化为中文数字
export const intToChinese = (value: number | string): string => {
  // 验证输入为非负整数
  const num = typeof value === "number" ? value : parseInt(value, 10);
  if (isNaN(num) || num < 0 || !Number.isInteger(num)) return "无效数字";
  if (num === 0) return "零";

  const str = String(num);
  const len = str.length - 1;
  const idxs = [
    "",
    "十",
    "百",
    "千",
    "万",
    "十",
    "百",
    "千",
    "亿",
    "十",
    "百",
    "千",
    "万",
    "十",
    "百",
    "千",
    "亿",
  ];
  const numChars = ["零", "一", "二", "三", "四", "五", "六", "七", "八", "九"];

  return str.replace(/([1-9]|0+)/g, ($, $1, idx) => {
    let pos = 0;
    if ($1[0] !== "0") {
      pos = len - idx;
      // 处理十位数特殊情况（如10→十，而非一十）
      if (idx === 0 && $1[0] === "1" && idxs[len - idx] === "十") {
        return idxs[len - idx];
      }
      return numChars[Number($1[0])] + idxs[len - idx];
    } else {
      const left = len - idx;
      const right = len - idx + $1.length;
      if (Math.floor(right / 4) - Math.floor(left / 4) > 0) {
        pos = left - (left % 4);
      }
      if (pos) {
        return idxs[pos] + numChars[Number($1[0])];
      } else if (idx + $1.length >= len) {
        return "";
      } else {
        return numChars[Number($1[0])];
      }
    }
  });
};

/**
 * 存储操作工具
 */
// localStorage - 设置
export const localStorageSet = (key: string, value: any): void => {
  if (!key) return;
  const val = typeof value !== "string" ? JSON.stringify(value) : value;
  window.localStorage.setItem(key, val);
};

// localStorage - 获取
export const localStorageGet = <T = string>(key: string): T | null => {
  if (!key) return null;
  const val = window.localStorage.getItem(key);
  if (val === null) return null;
  // 尝试解析JSON（失败则返回原始字符串）
  try {
    return JSON.parse(val) as T;
  } catch {
    return val as unknown as T;
  }
};

// localStorage - 删除
export const localStorageRemove = (key: string): void => {
  if (!key) return;
  window.localStorage.removeItem(key);
};

// sessionStorage - 设置
export const sessionStorageSet = (key: string, value: any): void => {
  if (!key) return;
  const val = typeof value !== "string" ? JSON.stringify(value) : value;
  window.sessionStorage.setItem(key, val);
};

// sessionStorage - 获取
export const sessionStorageGet = <T = string>(key: string): T | null => {
  if (!key) return null;
  const val = window.sessionStorage.getItem(key);
  if (val === null) return null;
  try {
    return JSON.parse(val) as T;
  } catch {
    return val as unknown as T;
  }
};

// sessionStorage - 删除
export const sessionStorageRemove = (key: string): void => {
  if (!key) return;
  window.sessionStorage.removeItem(key);
};

// 永久缓存 - 设置
export const permanentStorageSet = (key: string, value: any): void => {
  if (!key) return;
  const val = typeof value !== "string" ? JSON.stringify(value) : value;
  const expireTime = new Date("2099-12-31").getTime(); // 设置一个远期时间
  const item = {
    value: val,
    expire: expireTime,
  };
  window.localStorage.setItem(key, JSON.stringify(item));
};

// 永久缓存 - 获取
export const permanentStorageGet = (key: string): string | null => {
  if (!key) return null;
  const itemStr = window.localStorage.getItem(key);
  if (!itemStr) return null;

  try {
    const item = JSON.parse(itemStr);
    return item.value;
  } catch (e) {
    return null;
  }
};

// 永久缓存 - 删除
export const permanentStorageRemove = (key: string): void => {
  if (!key) return;
  window.localStorage.removeItem(key);
};

/**
 * Cookie操作工具
 */
// 设置Cookie
export const setCookie = (
  key: string,
  value: string,
  expireDays: number,
  path = "/"
): void => {
  if (!key) return;
  const d = new Date();
  d.setTime(d.getTime() + expireDays * 24 * 60 * 60 * 1000);
  const expires = `expires=${d.toUTCString()}`;
  // 编码key和value，避免特殊字符
  document.cookie = `${encodeURIComponent(key)}=${encodeURIComponent(
    value
  )};${expires};path=${path}`;
};

// 读取Cookie
export const getCookie = (key: string): string => {
  if (!key) return "";
  const name = `${encodeURIComponent(key)}=`;
  const decodedCookie = decodeURIComponent(document.cookie);
  const ca = decodedCookie.split(";");
  for (let i = 0; i < ca.length; i++) {
    let c = ca[i].trim();
    if (c.indexOf(name) === 0) {
      return c.substring(name.length);
    }
  }
  return "";
};

// 删除Cookie
export const delCookie = (key: string): void => {
  document.cookie = `${encodeURIComponent(key)}=;expires=${new Date()}`;
};

/**
 * 格式校验工具
 */
// 校验身份证号码
export const checkCardNo = (value: string): boolean => {
  // 15位纯数字或18位（最后一位可为X/x）
  return /(^\d{15}$)|(^\d{17}(\d|X|x)$)/.test(value.trim());
};

// 校验是否包含中文
export const haveCNChars = (value: string): boolean => {
  return /[\u4e00-\u9fa5]/.test(value);
};

// 校验是否为中国大陆的邮政编码
export const isPostCode = (value: number | string): boolean => {
  return /^[1-9][0-9]{5}$/.test(value.toString());
};

// 校验是否为IPv6地址
export const isIPv6 = (str: string): boolean => {
  if (typeof str !== "string") return false;
  // 简化版：匹配8组1-4位十六进制数，或压缩格式（::）
  return (
    /^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$/.test(str) ||
    /^([0-9a-fA-F]{1,4}:){1,7}:$/.test(str) ||
    /^([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}$/.test(str) ||
    /^([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}$/.test(str)
  );
};

// 校验是否为邮箱地址
export const isEmail = (value: string): boolean => {
  return (
    typeof value === "string" &&
    /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(value.trim())
  );
};

// 校验是否为中国大陆手机号
export const isTel = (value: number | string): boolean => {
  return /^1[3-9][0-9]{9}$/.test(value.toString());
};

// 校验是否包含emoji表情
export const isEmojiCharacter = (value: any): boolean => {
  const str = String(value);
  for (let i = 0; i < str.length; i++) {
    const hs = str.charCodeAt(i);
    if (0xd800 <= hs && hs <= 0xdbff) {
      if (str.length > 1) {
        const ls = str.charCodeAt(i + 1);
        const uc = (hs - 0xd800) * 0x400 + (ls - 0xdc00) + 0x10000;
        if (0x1d000 <= uc && uc <= 0x1f77f) return true;
      }
    } else if (str.length > 1) {
      const ls = str.charCodeAt(i + 1);
      if (ls === 0x20e3) return true;
    } else {
      if (0x2100 <= hs && hs <= 0x27ff) return true;
      if (0x2b05 <= hs && hs <= 0x2b07) return true;
      if (0x2934 <= hs && hs <= 0x2935) return true;
      if (0x3297 <= hs && hs <= 0x3299) return true;
      if (
        [0xa9, 0xae, 0x303d, 0x3030, 0x2b55, 0x2b1c, 0x2b1b, 0x2b50].includes(
          hs
        )
      )
        return true;
    }
  }
  return false;
};

/**
 * URL操作工具
 */
/**
 * 解析URL中的查询参数，返回结构化对象
 * @param url 可选，需要解析的URL，默认当前页面URL
 * @returns 解析后的参数对象（支持数字转换、重复参数数组化、URL解码）
 */
export const getUrlParams = (
  url: string = window.location.href
): Record<string, any> => {
  try {
    // 处理相对URL：以当前页面URL为基准解析（解决仅传"?a=1"或"/path"的情况）
    const baseUrl = window.location.href;
    const urlObj = new URL(url, baseUrl); // 第二个参数为基准URL
    const paramsStr = urlObj.search.slice(1);

    if (!paramsStr) return {};

    const paramsObj: Record<string, any> = {};
    paramsStr.split("&").forEach((param) => {
      const [key, val = ""] = param.split("=");
      const decodeKey = decodeURIComponent(key);
      if (decodeKey === "") return; // 过滤空键

      const decodeVal = decodeURIComponent(val);
      const finalVal = /^\d+$/.test(decodeVal) ? Number(decodeVal) : decodeVal;

      if (paramsObj.hasOwnProperty(decodeKey)) {
        paramsObj[decodeKey] = Array.isArray(paramsObj[decodeKey])
          ? [...paramsObj[decodeKey], finalVal]
          : [paramsObj[decodeKey], finalVal];
      } else {
        paramsObj[decodeKey] = finalVal;
      }
    });
    console.log("paramsObj:", paramsObj);
    return paramsObj;
  } catch (error) {
    // 捕获URL解析错误，返回空对象并提示
    console.error("URL解析失败，无效的URL:", url, "错误信息:", error);
    return {};
  }
};

/**
 * 对象转换为URL查询参数字符串
 * @param obj 要转换的对象（支持平面对象，值可为字符串、数字、布尔值、数组）
 * @param options 配置项
 * @param options.arrayFormat 数组格式，'repeat'（默认，重复键）| 'brackets'（带[]，如key[]=1）
 * @param options.ignoreNull 是否忽略值为null/undefined的键（默认true）
 * @returns 拼接后的URL参数字符串（不含开头的?）
 */
export const objToUrlParams = (
  obj: Record<string, any>,
  options: {
    arrayFormat?: "repeat" | "brackets";
    ignoreNull?: boolean;
  } = {}
): string => {
  const { arrayFormat = "repeat", ignoreNull = true } = options;
  const params: string[] = [];

  // 遍历对象键值对
  Object.entries(obj).forEach(([key, value]) => {
    // 忽略null/undefined（如果配置了）
    if (ignoreNull && (value === null || value === undefined)) {
      return;
    }

    // 处理数组
    if (Array.isArray(value)) {
      value.forEach((item) => {
        // 数组项为空时跳过
        if (ignoreNull && (item === null || item === undefined)) return;
        // 处理数组键格式（repeat：重复键；brackets：key[]）
        const arrayKey = arrayFormat === "brackets" ? `${key}[]` : key;
        params.push(
          `${encodeURIComponent(arrayKey)}=${encodeURIComponent(String(item))}`
        );
      });
      return;
    }

    // 处理非数组值（转为字符串后编码）
    params.push(
      `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`
    );
  });

  // 拼接成参数字符串（如 key1=val1&key2=val2）
  return params.join("&");
};

/**
 * URL参数解析为对象（自动转换数组、布尔值、数字类型）
 * @param url 可选，待解析的URL（默认当前页面URL）
 * @returns 解析后的对象（键值对对应参数，数组/布尔值/数字自动转换）
 */
export const urlParamsToObj = (
  url: string = window.location.href
): Record<string, any> => {
  try {
    // 解析URL，提取查询参数部分（?后面的内容）
    const urlObj = new URL(url, window.location.href); // 支持相对URL
    const paramsStr = urlObj.search.slice(1); // 去除开头的?

    if (!paramsStr) return {}; // 无参数时返回空对象

    const result: Record<string, any> = {};

    // 分割参数列表（按&拆分）
    paramsStr.split("&").forEach((param) => {
      // 分割键和值（兼容无值的参数，如?active → [active, '']）
      const [rawKey, rawVal = ""] = param.split("=");
      // 解码键和值（处理URL编码的特殊字符，如%20→空格、中文编码等）
      const key = decodeURIComponent(rawKey);
      const val = decodeURIComponent(rawVal);

      // 过滤空键（如URL中&&导致的空字符串键）
      if (key === "") return;

      // 类型转换：先判断布尔值，再判断数字，否则保持字符串
      let parsedVal: any;
      if (val === "true") {
        parsedVal = true; // 字符串"true" → 布尔值true
      } else if (val === "false") {
        parsedVal = false; // 字符串"false" → 布尔值false
      } else if (/^\d+$/.test(val)) {
        parsedVal = Number(val); // 纯数字字符串 → 数字（如"25"→25）
      } else if (val === "") {
        parsedVal = ""; // 空值保持空字符串（如?empty= → 空字符串）
      } else {
        parsedVal = val; // 其他情况保持字符串（如"abc"、"v1.0"）
      }

      // 处理数组：相同key的参数合并为数组
      if (Object.hasOwn(result, key)) {
        // 若已存在，判断是否为数组，不是则转为数组后添加新值
        result[key] = Array.isArray(result[key])
          ? [...result[key], parsedVal]
          : [result[key], parsedVal];
      } else {
        // 首次出现，直接赋值
        result[key] = parsedVal;
      }
    });

    return result;
  } catch (error) {
    console.error("URL参数解析失败：", error);
    return {};
  }
};

// 检测URL是否有效（仅浏览器环境，需兼容IE时慎用）
export const checkUrlValid = (URL: string): Promise<boolean> => {
  return new Promise((resolve) => {
    if (typeof (window as any).ActiveXObject !== "undefined") {
      const xmlhttp = new (window as any).ActiveXObject("microsoft.xmlhttp");
      xmlhttp.Open("GET", URL, false);
      try {
        xmlhttp.Send();
        resolve(xmlhttp.Status === 200);
      } catch (e) {
        resolve(false);
      }
    } else {
      fetch(URL, { method: "HEAD" })
        .then((res) => resolve(res.ok))
        .catch(() => resolve(false));
    }
  });
};

/**
 * 修改或添加 URL 中的指定参数
 * @param paramName 要修改的参数名
 * @param replaceWith 新的参数值（自动编码特殊字符）
 * @param url 可选，目标 URL，默认使用当前页面 URL
 * @returns 处理后的新 URL
 */
export const replaceUrlParam = (
  paramName: string,
  replaceWith: string,
  url: string = window.location.href
): string => {
  // 分离 URL 和哈希部分（参数只能在哈希前）
  const [baseUrl, hash] = url.split("#");
  const hasHash = !!hash;

  // 编码参数值（处理特殊字符）
  const encodedValue = encodeURIComponent(replaceWith);

  // 正则：精确匹配参数（避免匹配类似参数名，如 param vs param1）
  const paramRegex = new RegExp(`(\\?|&)${paramName}=([^&]*)`, "i");

  let newBaseUrl: string;

  if (paramRegex.test(baseUrl)) {
    // 替换已有参数
    newBaseUrl = baseUrl.replace(paramRegex, `$1${paramName}=${encodedValue}`);
  } else {
    // 添加新参数（判断是否已有 ?）
    const separator = baseUrl.includes("?") ? "&" : "?";
    newBaseUrl = `${baseUrl}${separator}${paramName}=${encodedValue}`;
  }

  // 拼接哈希部分（如果有）
  return hasHash ? `${newBaseUrl}#${hash}` : newBaseUrl;
};
// 删除URL中指定参数
export const deleteUrlParam = (
  name: string,
  url: string = window.location.href
): string => {
  const urlObj = new URL(url);
  urlObj.searchParams.delete(name); // 用URLSearchParams API简化逻辑
  return urlObj.toString();
};

/**
 * 设备判断工具
 */
// 判断是移动还是PC设备
export const isMobileDevice = (): "mobile" | "desktop" => {
  if (
    navigator.userAgent.match(
      /(iPhone|iPod|Android|ios|iOS|iPad|Backerry|WebOS|Symbian|Windows Phone|Phone)/i
    )
  ) {
    return "mobile";
  }
  return "desktop";
};

// 判断是否是苹果移动设备
export const isAppleMobileDevice = (): boolean => {
  const reg = /iphone|ipod|ipad|Macintosh/i;
  return reg.test(navigator.userAgent.toLowerCase());
};

// 判断是否是安卓移动设备
export const isAndroidMobileDevice = (): boolean => {
  return /android/i.test(navigator.userAgent.toLowerCase());
};

// 判断是Windows还是Mac系统
export const getOsType = ():
  | "windows"
  | "mac"
  | "linux"
  | "ios"
  | "android"
  | string => {
  const ua = navigator.userAgent.toLowerCase();
  if (/windows/.test(ua)) return "windows";
  if (/macintosh|mac os x/.test(ua)) return "mac";
  if (/linux/.test(ua)) return "linux";
  if (/iphone|ipad|ipod/.test(ua)) return "ios";
  if (/android/.test(ua)) return "android";
  return ua;
};

// 判断浏览器类型
export const getBrowserType = ():
  | "weixin"
  | "qqbrowser"
  | "edge"
  | "chrome"
  | "firefox"
  | "safari"
  | false => {
  const ua = navigator.userAgent.toLowerCase();
  if (ua.includes("micromessenger")) return "weixin";
  if (ua.includes("qqbrowser")) return "qqbrowser";
  if (ua.includes("edge")) return "edge";
  if (ua.includes("chrome") && !ua.includes("edge")) return "chrome";
  if (ua.includes("firefox")) return "firefox";
  if (ua.includes("safari") && !ua.includes("chrome")) return "safari";
  return false;
};
// 浏览器型号和版本
export const getExplorerInfo = (): { type: string; version: number } => {
  const t = navigator.userAgent.toLowerCase();
  if (t.indexOf("msie") >= 0) {
    const version = Number(t.match(/msie ([\d]+)/)?.[1]);
    return { type: "IE", version: version || -1 };
  }
  if (t.match(/trident\/.+?rv:(([\d.]+))/)) return { type: "IE", version: 11 };
  if (t.indexOf("edge") >= 0) {
    const version = Number(t.match(/edge\/([\d]+)/)?.[1]);
    return { type: "Edge", version: version || -1 };
  }
  if (t.indexOf("firefox") >= 0) {
    const version = Number(t.match(/firefox\/([\d]+)/)?.[1]);
    return { type: "Firefox", version: version || -1 };
  }
  if (t.indexOf("chrome") >= 0) {
    const version = Number(t.match(/chrome\/([\d]+)/)?.[1]);
    return { type: "Chrome", version: version || -1 };
  }
  if (t.indexOf("opera") >= 0) {
    const version = Number(t.match(/opera.([\d]+)/)?.[1]);
    return { type: "Opera", version: version || -1 };
  }
  if (t.indexOf("safari") >= 0) {
    const version = Number(t.match(/version\/([\d]+)/)?.[1]);
    return { type: "Safari", version: version || -1 };
  }
  return { type: t, version: -1 };
};

/**
 * 浏览器操作工具
 */
// 滚动到页面顶部
export const scrollToTop = (): void => {
  const height = document.documentElement.scrollTop || document.body.scrollTop;
  if (height > 0) {
    window.requestAnimationFrame(scrollToTop);
    window.scrollTo(0, height - height / 8);
  }
};

// 滚动到页面底部
export const scrollToBottom = (): void => {
  window.scrollTo(0, document.documentElement.scrollHeight);
};

// 滚动到指定元素区域
export const smoothScroll = (selector: string): void => {
  const element = document.querySelector(selector);
  if (element) {
    element.scrollIntoView({ behavior: "smooth" });
  }
};

// 获取可视窗口高度
export const getClientHeight = (): number => {
  if (document.body.clientHeight && document.documentElement.clientHeight) {
    return Math.min(
      document.body.clientHeight,
      document.documentElement.clientHeight
    );
  }
  return document.body.clientHeight || document.documentElement.clientHeight;
};

// 获取可视窗口宽度
export const getPageViewWidth = (): number => {
  return (
    document.compatMode === "BackCompat"
      ? document.body
      : document.documentElement
  ).clientWidth;
};

// 打开浏览器全屏
export const toFullScreen = (): void => {
  const element = document.body;
  if (element.requestFullscreen) {
    element.requestFullscreen();
  } else if ((element as any).mozRequestFullScreen) {
    (element as any).mozRequestFullScreen();
  } else if ((element as any).msRequestFullscreen) {
    (element as any).msRequestFullscreen();
  } else if ((element as any).webkitRequestFullscreen) {
    (element as any).webkitRequestFullScreen();
  }
};

// 退出浏览器全屏
export const exitFullscreen = (): void => {
  if (document.exitFullscreen) {
    document.exitFullscreen();
  } else if ((document as any).msExitFullscreen) {
    (document as any).msExitFullscreen();
  } else if ((document as any).mozCancelFullScreen) {
    (document as any).mozCancelFullScreen();
  } else if ((document as any).webkitExitFullscreen) {
    (document as any).webkitExitFullscreen();
  }
};

// 复制文本到剪贴板
export const copyText = (text: string): Promise<boolean> => {
  return new Promise((resolve) => {
    if (navigator.clipboard && window.isSecureContext) {
      // 使用现代 Clipboard API
      navigator.clipboard
        .writeText(text)
        .then(() => {
          resolve(true);
        })
        .catch(() => {
          // 如果现代API失败，使用传统方法
          legacyCopyText(text, resolve);
        });
    } else {
      // 使用传统方法
      legacyCopyText(text, resolve);
    }
  });
};

// 传统复制文本方法
const legacyCopyText = (
  text: string,
  callback: (result: boolean) => void
): void => {
  try {
    const textArea = document.createElement("textarea");
    textArea.value = text;

    // 避免滚动到底部
    textArea.style.top = "0";
    textArea.style.left = "0";
    textArea.style.position = "fixed";
    textArea.style.opacity = "0";

    document.body.appendChild(textArea);
    textArea.focus();
    textArea.select();

    const successful = document.execCommand("copy");
    document.body.removeChild(textArea);
    callback(successful);
  } catch (err) {
    callback(false);
  }
};

// 图片懒加载
export const lazyLoadImage = (
  imgElement: HTMLImageElement,
  src: string,
  placeholder?: string
): void => {
  // 设置占位符
  if (placeholder) {
    imgElement.src = placeholder;
  }

  // 创建观察器
  const imageObserver = new IntersectionObserver((entries, observer) => {
    entries.forEach((entry) => {
      if (entry.isIntersecting) {
        const img = entry.target as HTMLImageElement;
        img.src = src;
        img.onload = () => {
          // 移除观察器
          observer.unobserve(img);
        };
      }
    });
  });

  imageObserver.observe(imgElement);
};

// 判断是否需要刷新页面
export const isPageRefresh = (): boolean => {
  if (performance.navigation) {
    return performance.navigation.type === 1; // 1 表示页面通过刷新或重新加载加载
  }
  return false;
};

// 强制刷新页面
export const forceRefresh = (): void => {
  window.location.reload();
};

// 打开内部链接
export const openInternalLink = (
  path: string,
  target: string = "_self"
): void => {
  const url = new URL(path, window.location.origin);
  window.open(url.href, target);
};

// 打开外部链接
export const openExternalLink = (
  url: string,
  target: string = "_blank"
): void => {
  window.open(url, target, "noopener,noreferrer");
};

/**
 * 时间操作工具
 */
// 当前时间（格式化）
export const nowTime = (): string => {
  const now = new Date();
  const year = now.getFullYear();
  const month = now.getMonth() + 1;
  const date = now.getDate() >= 10 ? now.getDate() : `0${now.getDate()}`;
  const hour = now.getHours() >= 10 ? now.getHours() : `0${now.getHours()}`;
  const miu =
    now.getMinutes() >= 10 ? now.getMinutes() : `0${now.getMinutes()}`;
  const sec =
    now.getSeconds() >= 10 ? now.getSeconds() : `0${now.getSeconds()}`;
  return `${year}年${month}月${date}日 ${hour}:${miu}:${sec}`;
};

// 格式化时间
export const dateFormater = (
  formater: string,
  time?: number | string | Date
): string => {
  const date = time ? new Date(time) : new Date();
  if (isNaN(date.getTime())) return "无效时间";
  const Y = date.getFullYear() + "";
  const M = date.getMonth() + 1;
  const D = date.getDate();
  const H = date.getHours();
  const m = date.getMinutes();
  const s = date.getSeconds();

  return formater
    .replace(/YYYY|yyyy/g, Y)
    .replace(/YY|yy/g, Y.substr(2, 2))
    .replace(/MM/g, M < 10 ? `0${M}` : `${M}`)
    .replace(/DD/g, D < 10 ? `0${D}` : `${D}`)
    .replace(/HH|hh/g, H < 10 ? `0${H}` : `${H}`)
    .replace(/mm/g, m < 10 ? `0${m}` : `${m}`)
    .replace(/ss/g, s < 10 ? `0${s}` : `${s}`);
};

// 以当前时间为准的时间范围计算
export const getDateRange = (
  type: "today" | "week" | "month" | "threeMonths" | "year" | "threeYears"
): { start: Date; end: Date } => {
  const now = new Date();
  const start = new Date(now);
  const end = new Date(now);

  switch (type) {
    case "today":
      start.setHours(0, 0, 0, 0);
      end.setHours(23, 59, 59, 999);
      break;
    case "week":
      // 当前周（周日到周六）
      const day = start.getDay(); // 0 (Sunday) to 6 (Saturday)
      start.setDate(start.getDate() - day);
      start.setHours(0, 0, 0, 0);
      end.setDate(end.getDate() + (6 - day));
      end.setHours(23, 59, 59, 999);
      break;
    case "month":
      // 当前月
      start.setDate(1);
      start.setHours(0, 0, 0, 0);
      end.setMonth(end.getMonth() + 1, 0);
      end.setHours(23, 59, 59, 999);
      break;
    case "threeMonths":
      // 近三个月（过去三个月）
      start.setMonth(start.getMonth() - 3);
      start.setHours(0, 0, 0, 0);
      end.setHours(23, 59, 59, 999);
      break;
    case "year":
      // 当前年
      start.setMonth(0, 1);
      start.setHours(0, 0, 0, 0);
      end.setMonth(11, 31);
      end.setHours(23, 59, 59, 999);
      break;
    case "threeYears":
      // 近三年（过去三年）
      start.setFullYear(start.getFullYear() - 3);
      start.setHours(0, 0, 0, 0);
      end.setHours(23, 59, 59, 999);
      break;
  }

  return { start, end };
};

// 前后时间范围计算（支持过去和将来）
export const getDateRangeAround = (
  type: "week" | "month" | "threeMonths" | "year" | "threeYears",
  direction: "past" | "future" | "both" = "both"
): { start: Date; end: Date } => {
  const now = new Date();
  const start = new Date(now);
  const end = new Date(now);

  // 设置时间为当天开始
  start.setHours(0, 0, 0, 0);
  end.setHours(23, 59, 59, 999);

  switch (type) {
    case "week":
      if (direction === "past" || direction === "both") {
        // 过去一周
        start.setDate(start.getDate() - 7);
      }
      if (direction === "future" || direction === "both") {
        // 将来一周
        end.setDate(end.getDate() + 7);
      }
      break;
    case "month":
      if (direction === "past" || direction === "both") {
        // 过去一个月
        start.setMonth(start.getMonth() - 1);
      }
      if (direction === "future" || direction === "both") {
        // 将来一个月
        end.setMonth(end.getMonth() + 1);
      }
      break;
    case "threeMonths":
      if (direction === "past" || direction === "both") {
        // 过去三个月
        start.setMonth(start.getMonth() - 3);
      }
      if (direction === "future" || direction === "both") {
        // 将来三个月
        end.setMonth(end.getMonth() + 3);
      }
      break;
    case "year":
      if (direction === "past" || direction === "both") {
        // 过去一年
        start.setFullYear(start.getFullYear() - 1);
      }
      if (direction === "future" || direction === "both") {
        // 将来一年
        end.setFullYear(end.getFullYear() + 1);
      }
      break;
    case "threeYears":
      if (direction === "past" || direction === "both") {
        // 过去三年
        start.setFullYear(start.getFullYear() - 3);
      }
      if (direction === "future" || direction === "both") {
        // 将来三年
        end.setFullYear(end.getFullYear() + 3);
      }
      break;
  }

  return { start, end };
};

/**
 * 水印工具
 */
// 添加水印
export const addWatermark = (
  text: string,
  parentNode: HTMLElement = document.body
): HTMLElement => {
  // 处理中文/特殊字符：转换为UTF-8编码后再进行base64处理
  const encodeText = (str: string) => {
    return btoa(unescape(encodeURIComponent(str)));
  };

  // 生成SVG（压缩格式，避免多余空格/换行导致解析失败）
  const svg = `
    <svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">
      <text x="10" y="100" transform="rotate(-45 10,100)" fill="rgba(0,0,0,0.1)" font-size="16">${text}</text>
    </svg>
  `
    .replace(/\s+/g, " ")
    .trim(); // 压缩SVG：去除多余空格和换行

  // 创建水印容器
  const watermark = document.createElement("div");
  watermark.className = "watermark-container";

  // 设置样式（增强优先级，避免被覆盖）
  const style = document.createElement("style");
  style.innerHTML = `
    .watermark-container {
      position: fixed !important; /* 强制固定定位 */
      top: 0 !important;
      left: 0 !important;
      width: 100% !important;
      height: 100% !important;
      pointer-events: none !important; /* 不影响鼠标交互 */
      background-repeat: repeat !important;
      background-image: url("data:image/svg+xml;base64,${encodeText(
        svg
      )}") !important;
      z-index: 99999 !important; /* 确保在最顶层 */
      opacity: 1 !important; /* 防止被意外隐藏 */
    }
  `;

  // 添加到页面
  document.head.appendChild(style);
  parentNode.appendChild(watermark);

  // 调试：确认水印已添加到DOM
  console.log("水印已添加：", watermark);
  return watermark;
};

// 移除水印
export const removeWatermark = (): void => {
  const watermark = document.querySelector(".watermark-container");
  if (watermark) {
    watermark.remove();
  }

  // 移除样式
  const styles = document.head.querySelectorAll("style");
  styles.forEach((style) => {
    if (style.innerHTML.includes("watermark-container")) {
      style.remove();
    }
  });
};

/**
 * 通用操作工具
 */
// 阻止冒泡事件
export const stopPropagation = (e: Event): void => {
  e = e || window.event;
  if (e.stopPropagation) {
    e.stopPropagation();
  } else {
    (e as any).cancelBubble = true;
  }
};

// 防抖函数
export const debounce = <T extends (...args: any[]) => any>(
  fn: T,
  wait: number
): ((...args: Parameters<T>) => void) => {
  let timer: number | null = null;
  return function (this: ThisParameterType<T>, ...args: Parameters<T>) {
    if (timer) clearTimeout(timer);
    timer = window.setTimeout(() => fn.apply(this, args), wait);
  };
};

// 节流函数
export const throttle = <T extends (...args: any[]) => any>(
  fn: T,
  delay: number
): ((...args: Parameters<T>) => ReturnType<T> | void) => {
  let curTime = Date.now();
  return function (this: ThisParameterType<T>, ...args: Parameters<T>) {
    const context = this;
    const nowTime = Date.now();
    if (nowTime - curTime >= delay) {
      curTime = Date.now();
      return fn.apply(context, args);
    }
  };
};

/**
 * 正则表达式工具
 */
// 转义正则表达式特殊字符
export const escapeRegExp = (str: string): string => {
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
};

// 验证是否为有效的邮箱地址
export const isValidEmail = (email: string): boolean => {
  const re =
    /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
  return re.test(email);
};

// 验证是否为有效的手机号码
export const isValidPhone = (phone: string): boolean => {
  const re = /^1[3-9]\d{9}$/;
  return re.test(phone);
};

/**
 * UUID生成工具
 */
// 生成UUID (版本4)
export const generateUUID = (): string => {
  return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
    const r = (Math.random() * 16) | 0;
    const v = c === "x" ? r : (r & 0x3) | 0x8;
    return v.toString(16);
  });
};

/**
 * 数字格式化工具
 */
// 格式化数字为指定位数的小数
export const formatNumber = (num: number, digits: number): string => {
  return num.toFixed(digits);
};

// 将数字格式化为货币形式
export const formatCurrency = (num: number, symbol = "¥"): string => {
  return symbol + formatThousands(num);
};

/**
 * DOM操作增强工具
 */
// 获取元素的样式值
export const getStyle = (element: HTMLElement, styleName: string): string => {
  if (window.getComputedStyle) {
    return window.getComputedStyle(element, null).getPropertyValue(styleName);
  }
  // @ts-ignore 兼容IE
  return element.currentStyle[styleName];
};

// 设置元素的样式
export const setStyle = (
  element: HTMLElement,
  styles: Record<string, string>
): void => {
  for (const key in styles) {
    if (styles.hasOwnProperty(key)) {
      element.style.setProperty(key, styles[key]);
    }
  }
};

// 添加CSS类
export const addClass = (element: HTMLElement, className: string): void => {
  if (!hasClass(element, className)) {
    element.className = (element.className + " " + className).trim();
  }
};

// 移除CSS类
export const removeClass = (element: HTMLElement, className: string): void => {
  if (hasClass(element, className)) {
    const reg = new RegExp("(\\s|^)" + className + "(\\s|$)");
    element.className = element.className.replace(reg, " ").trim();
  }
};

// 判断元素是否包含某个CSS类
export const hasClass = (element: HTMLElement, className: string): boolean => {
  return (" " + element.className + " ").indexOf(" " + className + " ") > -1;
};

/**
 * 函数式编程工具
 */
// 柯里化函数
export const curry = (fn: Function): Function => {
  return function curried(this: any, ...args: any[]) {
    if (args.length >= fn.length) {
      return fn.apply(this, args);
    } else {
      return function (this: any, ...nextArgs: any[]) {
        return curried.apply(this, args.concat(nextArgs));
      };
    }
  };
};

// 组合函数
export const compose = <T>(...fns: Array<(arg: T) => T>): ((arg: T) => T) => {
  return (value: T) => fns.reduceRight((acc, fn) => fn(acc), value);
};

// 管道函数
export const pipe = <T>(...fns: Array<(arg: T) => T>): ((arg: T) => T) => {
  return (value: T) => fns.reduce((acc, fn) => fn(acc), value);
};

/**
 * 数据验证工具
 */
// 验证是否为空值（null, undefined, 空字符串, 空数组, 空对象）
export const isEmpty = (value: any): boolean => {
  if (value === null || value === undefined) return true;
  if (typeof value === "string" || Array.isArray(value))
    return value.length === 0;
  if (typeof value === "object") return Object.keys(value).length === 0;
  return false;
};

// 验证是否为有效的URL
export const isValidURL = (url: string): boolean => {
  try {
    new URL(url);
    return true;
  } catch (e) {
    return false;
  }
};

// 验证是否为有效的JSON字符串
export const isValidJSON = (str: string): boolean => {
  try {
    JSON.parse(str);
    return true;
  } catch (e) {
    return false;
  }
};

/**
 * 对象操作工具
 */
// 检查对象是否为空
export const isObjectEmpty = (obj: Record<string, any>): boolean => {
  return Object.keys(obj).length === 0;
};

// 获取对象的键值对数组
export const entries = <T>(obj: Record<string, T>): [string, T][] => {
  return Object.entries(obj);
};

// 获取对象的所有键
export const keys = (obj: Record<string, any>): string[] => {
  return Object.keys(obj);
};

// 获取对象的所有值
export const values = <T>(obj: Record<string, T>): T[] => {
  return Object.values(obj);
};

/**
 * 字符串处理增强工具
 */
// 截取指定长度的字符串，超出部分用省略号代替
export const ellipsis = (str: string, length: number): string => {
  if (str.length <= length) return str;
  return str.substring(0, length) + "...";
};

// 生成指定长度的随机字符串
export const generateRandomString = (length: number): string => {
  const chars =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
  let result = "";
  for (let i = 0; i < length; i++) {
    result += chars.charAt(Math.floor(Math.random() * chars.length));
  }
  return result;
};

// 字符串反转
export const reverseString = (str: string): string => {
  return str.split("").reverse().join("");
};

/**
 * 数组操作增强工具
 */
// 数组去重
export const uniqueArray = <T>(arr: T[]): T[] => {
  return [...new Set(arr)];
};

// 根据属性对对象数组进行排序
export const sortBy = <T>(
  arr: T[],
  key: keyof T,
  order: "asc" | "desc" = "asc"
): T[] => {
  return arr.sort((a, b) => {
    const aVal = a[key];
    const bVal = b[key];

    if (aVal < bVal) {
      return order === "asc" ? -1 : 1;
    }
    if (aVal > bVal) {
      return order === "asc" ? 1 : -1;
    }
    return 0;
  });
};

// 根据条件过滤数组
export const filterBy = <T>(arr: T[], key: keyof T, value: any): T[] => {
  return arr.filter((item) => item[key] === value);
};

/**
 * 通用工具
 */
// 等待指定时间（毫秒）
export const sleep = (ms: number): Promise<void> => {
  return new Promise((resolve) => setTimeout(resolve, ms));
};


/**
 * 准确判断数据类型
 * @param value 待判断的值
 * @returns 类型字符串（全小写，如 'array'、'date'、'regexp' 等）
 */
export const getType = (value: any): string => {
  // 处理 null（特殊情况，typeof null 会返回 'object'）
  if (value === null) return 'null';

  // 处理 undefined（typeof 直接返回 'undefined'，但统一用 toString 逻辑）
  if (value === undefined) return 'undefined';

  // 核心：用 Object.prototype.toString.call 获取类型标识
  // 结果格式为 "[object Type]"，如 "[object Array]"、"[object Date]"
  const typeStr = Object.prototype.toString.call(value);

  // 提取类型名（如从 "[object Array]" 中提取 "Array"）
  const type = typeStr.match(/^\[object (.*)\]$/)?.[1] || '';

  // 转为全小写返回（统一格式，如 'array'、'date'）
  return type.toLowerCase();
};



// 对象深拷贝
export const deepClone = <T = any>(obj: T, hash = new WeakMap()): T => {
  // 1. 处理基本类型（非对象）和 null
  if (obj === null || typeof obj !== "object") {
    return obj;
  }

  // 2. 处理循环引用（已克隆过的对象直接返回缓存）
  if (hash.has(obj as object)) {
    return hash.get(obj as object) as T;
  }

  // 3. 处理 Date 对象
  if (obj instanceof Date) {
    const clone = new Date(obj.getTime()) as unknown as T;
    hash.set(obj, clone);
    return clone;
  }

  // 4. 处理 RegExp 对象（保留源文本、标志和 lastIndex）
  if (obj instanceof RegExp) {
    const clone = new RegExp(
      obj.source,
      // 提取正则标志（g/i/m/u/y/s）
      (obj.flags || "") + (obj.sticky ? "y" : "") + (obj.unicode ? "u" : "")
    ) as unknown as T;
    clone.lastIndex = obj.lastIndex; // 复制 lastIndex
    hash.set(obj, clone);
    return clone;
  }

  // 5. 处理 Map 对象
  if (obj instanceof Map) {
    const clone = new Map() as any;
    hash.set(obj, clone);
    // 递归克隆键和值
    obj.forEach((value, key) => {
      clone.set(deepClone(key, hash), deepClone(value, hash));
    });
    return clone as T;
  }

  // 6. 处理 Set 对象
  if (obj instanceof Set) {
    const clone = new Set() as any;
    hash.set(obj, clone);
    // 递归克隆元素
    obj.forEach((value) => {
      clone.add(deepClone(value, hash));
    });
    return clone as T;
  }

  // 7. 处理 Error 及子类（如 TypeError、RangeError 等）
  if (obj instanceof Error) {
    const clone = new (obj.constructor as ErrorConstructor)(obj.message) as any;
    // 复制 Error 特有的属性
    clone.name = obj.name;
    clone.stack = obj.stack; // 复制堆栈信息（部分环境支持）
    hash.set(obj, clone);
    return clone as T;
  }

  // 8. 处理 ArrayBuffer（二进制数据）
  if (obj instanceof ArrayBuffer) {
    const clone = obj.slice() as unknown as T;
    hash.set(obj, clone);
    return clone;
  }

  // 9. 处理 TypedArray（如 Uint8Array、Float64Array 等）
  if (ArrayBuffer.isView(obj)) {
    const clone = new (obj.constructor as any)(obj.buffer.slice()) as T;
    hash.set(obj, clone);
    return clone;
  }

  // 10. 处理函数（不建议克隆函数，此处返回原函数，或可根据需求改为序列化后解析）
  if (typeof obj === "function") {
    // 注意：函数克隆复杂且意义有限，此处直接返回原函数
    return obj;
  }

  // 11. 处理普通对象/数组（包括自定义类实例）
  // 数组：创建新数组；对象：继承原对象原型
  const cloneObj = Array.isArray(obj)
    ? []
    : Object.create(Object.getPrototypeOf(obj));

  // 缓存克隆对象，避免循环引用
  hash.set(obj as object, cloneObj);

  // 12. 复制所有自有键（包括字符串键和 Symbol 键）
  Reflect.ownKeys(obj as object).forEach((key) => {
    // 获取原对象的属性描述符（确保复制 get/set 等特性）
    const descriptor = Object.getOwnPropertyDescriptor(obj as object, key);
    if (descriptor) {
      // 递归克隆属性值
      const clonedValue = deepClone((obj as any)[key], hash);
      // 用原属性描述符定义克隆对象的属性（保留 configurable/enumerable/writable 等特性）
      Object.defineProperty(cloneObj, key, {
        ...descriptor,
        value: clonedValue,
      });
    }
  });

  return cloneObj as T;
};

/**
 * 数学计算工具
 */
// 获取数组中的最大值
export const arrayMax = (arr: number[]): number => Math.max(...arr);

// 获取数组中的最小值
export const arrayMin = (arr: number[]): number => Math.min(...arr);

// 求数组的平均值
export const arrayAverage = (arr: number[]): number =>
  arr.reduce((a, b) => a + b, 0) / arr.length;

// 数组求和
export const arraySum = (arr: number[]): number =>
  arr.reduce((a, b) => a + b, 0);

/**
 * 设备信息工具
 */
// 获取设备像素比
export const getDevicePixelRatio = (): number => {
  return window.devicePixelRatio || 1;
};

// 获取屏幕方向
export const getScreenOrientation = ():
  | "portrait-primary"
  | "landscape-primary"
  | "portrait-secondary"
  | "landscape-secondary"
  | string => {
  if (window.screen.orientation) {
    return window.screen.orientation.type;
  }
  // 兼容性处理
  return window.orientation === 0 || window.orientation === 180
    ? "portrait"
    : "landscape";
};

/**
 * 判断当前设备具体类型
 * @returns 设备类型字符串：'ios'|'android'|'tablet'|'pc'
 * - 'ios'：iPhone、iPod 等 iOS 移动设备
 * - 'android'：安卓手机等移动设备
 * - 'tablet'：iPad、安卓平板等平板设备
 * - 'pc'：Windows、Mac 等桌面设备
 */
export const getDeviceType = (): "ios" | "android" | "tablet" | "pc" => {
  const userAgent = navigator.userAgent.toLowerCase();
  // 排除桌面设备UA中的移动标识
  const isDesktop =
    /windows|macintosh|linux/.test(userAgent) &&
    !/mobile|tablet|phone/.test(userAgent);
  if (isDesktop) return "pc";

  // 平板判断
  const isTablet =
    /ipad|tablet|pad|playbook|silk/.test(userAgent) ||
    (/android/.test(userAgent) && !/mobile/.test(userAgent));
  if (isTablet) return "tablet";

  // 手机判断
  if (/iphone|ipod/.test(userAgent)) return "ios";
  if (/android/.test(userAgent) && /mobile/.test(userAgent)) return "android";

  return "pc";
};

/**
 * 剪贴板工具
 */
// 读取剪贴板内容
export const readClipboard = async (): Promise<string> => {
  try {
    if (navigator.clipboard && navigator.clipboard.readText) {
      const text = await navigator.clipboard.readText();
      return text;
    }
    // 如果 Clipboard API 不可用，返回空字符串
    return "";
  } catch (err) {
    console.error("读取剪贴板失败:", err);
    return "";
  }
};

/**
 * 浏览器通知工具
 */
// 显示浏览器通知
export const showNotification = async (
  title: string,
  options?: NotificationOptions
): Promise<boolean> => {
  if (!("Notification" in window)) {
    console.warn("浏览器不支持通知功能");
    return false;
  }

  try {
    if (Notification.permission === "granted") {
      new Notification(title, options);
      return true;
    } else if (Notification.permission !== "denied") {
      const permission = await Notification.requestPermission();
      if (permission === "granted") {
        new Notification(title, options);
        return true;
      }
    }
    return false;
  } catch (err) {
    console.error("显示通知时出错:", err);
    return false;
  }
};

/**
 * 密码强度检测工具
 */
// 检查密码强度 (返回0-5的数值，0为最弱，5为最强)
export const checkPasswordStrength = (password: string): number => {
  let strength = 0;
  // 长度至少8位
  if (password.length >= 8) strength++;
  // 包含小写字母
  if (/[a-z]/.test(password)) strength++;
  // 包含大写字母
  if (/[A-Z]/.test(password)) strength++;
  // 包含数字
  if (/\d/.test(password)) strength++;
  // 包含特殊字符
  if (/[^A-Za-z0-9]/.test(password)) strength++;
  return strength;
};

/**
 * 身份证号工具
 */
// 根据身份证号获取生日
export const getBirthByIdCard = (idCard: string): string => {
  let birthday = "";
  if (idCard.length === 18) {
    birthday = idCard.substring(6, 14);
  } else if (idCard.length === 15) {
    birthday = "19" + idCard.substring(6, 12);
  }
  return birthday.replace(/(.{4})(.{2})(.{2})/, "$1-$2-$3");
};

// 根据身份证号获取性别
export const getSexByIdCard = (idCard: string): string => {
  let sex = "";
  if (idCard.length === 18) {
    sex = idCard.substring(16, 17);
  } else if (idCard.length === 15) {
    sex = idCard.substring(14, 15);
  }
  return parseInt(sex) % 2 === 0 ? "女" : "男";
};

/**
 * 颜色转换工具
 */
// RGB转十六进制
export const rgbToHex = (r: number, g: number, b: number): string => {
  return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
};

// 十六进制转RGB
export const hexToRgb = (
  hex: string
): { r: number; g: number; b: number } | null => {
  const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
  return result
    ? {
        r: parseInt(result[1], 16),
        g: parseInt(result[2], 16),
        b: parseInt(result[3], 16),
      }
    : null;
};

/**
 * 带过期时间的localStorage工具
 */
// 设置带过期时间的localStorage
export const setStorageWithExpire = (
  key: string,
  value: any,
  expireMs: number
): void => {
  if (!key || expireMs <= 0) return;
  const data = {
    value: typeof value !== "string" ? JSON.stringify(value) : value,
    expire: Date.now() + expireMs,
  };
  window.localStorage.setItem(key, JSON.stringify(data));
};

// 获取带过期时间的localStorage
export const getStorageWithExpire = <T = string>(key: string): T | null => {
  if (!key) return null;
  const dataStr = window.localStorage.getItem(key);
  if (!dataStr) return null;

  try {
    const data = JSON.parse(dataStr);
    if (data.expire < Date.now()) {
      window.localStorage.removeItem(key);
      return null;
    }
    try {
      return JSON.parse(data.value) as T;
    } catch {
      return data.value as unknown as T;
    }
  } catch {
    return null;
  }
};

/**
 * 性能监控工具
 */
// 获取页面加载时间
export const getPageLoadTime = (): number | null => {
  if ("performance" in window) {
    const perfData = window.performance.timing;
    return perfData.loadEventEnd - perfData.navigationStart;
  }
  return null;
};

/**
 * 文件处理工具
 */
// 格式化文件大小
export const formatFileSize = (bytes: number): string => {
  if (bytes === 0) return "0 B";
  const k = 1024;
  const sizes = ["B", "KB", "MB", "GB", "TB"];
  const i = Math.floor(Math.log(bytes) / Math.log(k));
  return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
};

// 下载文本为文件
export const downloadTextAsFile = (content: string, filename: string): void => {
  const blob = new Blob([content], { type: "text/plain;charset=utf-8" });
  const url = URL.createObjectURL(blob);
  const a = document.createElement("a");
  a.href = url;
  a.download = filename;
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
  URL.revokeObjectURL(url);
};

/**
 * 网络状态工具
 */
// 检查网络是否在线
export const isOnline = (): boolean => {
  return navigator.onLine;
};

// 监听网络状态变化
export const onNetworkStatusChange = (
  callback: (isOnline: boolean) => void
): void => {
  window.addEventListener("online", () => callback(true));
  window.addEventListener("offline", () => callback(false));
};

/**
 * 元素操作工具
 */
// 检查元素是否在视口内
export const isElementInViewport = (el: HTMLElement): boolean => {
  const rect = el.getBoundingClientRect();
  return (
    rect.top >= 0 &&
    rect.left >= 0 &&
    rect.bottom <=
      (window.innerHeight || document.documentElement.clientHeight) &&
    rect.right <= (window.innerWidth || document.documentElement.clientWidth)
  );
};

// 获取元素到视口的距离
export const getElementOffset = (
  el: HTMLElement
): { top: number; left: number } => {
  const rect = el.getBoundingClientRect();
  return {
    top: rect.top + window.scrollY,
    left: rect.left + window.scrollX,
  };
};
